package com.bto.solar.work.service;

import cn.hutool.core.lang.Validator;
import com.alibaba.fastjson2.JSONArray;
import com.bto.solar.work.common.BizException;
import com.bto.solar.work.common.BizLoginException;
import com.bto.solar.work.common.RedisService;
import com.bto.solar.work.config.ConfigParams;
import com.bto.solar.work.constant.BizCode;
import com.bto.solar.work.constant.Constants;
import com.bto.solar.work.constant.DBConstants;
import com.bto.solar.work.constant.TokenConstants;
import com.bto.solar.work.dao.IUserService;
import com.bto.solar.work.dto.ApiLoginReq;
import com.bto.solar.work.dto.ApiLoginRes;
import com.bto.solar.work.entity.User;
import com.bto.solar.work.utils.JwtUtils;
import com.bto.solar.work.utils.NullUtils;
import com.bto.solar.work.utils.ServletUtils;
import com.bto.solar.work.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author 王小波
 * @description: 登陆认证
 * @date 2023/7/11 14:42
 */

@Service
public class BtoLoginService {

    @Value("${bto.allow-update-order-info-user-ids}")
    private String loginUserIds;
    @Value("${bto.login-role-name-list}")
    private String loginRoleNames;
    @Value("${bto.login-permissions-name-list}")
    private String loginPermissionsNames;

    @Autowired
    private ConfigParams configParams;



    @Autowired
    private RedisService redisService;
    @Autowired
    private BtoUserService btoUserService;
    @Autowired
    @Lazy
    private IUserService userService;

    /**
     * APP登录
     */
    public ApiLoginRes apiLogin(ApiLoginReq loginReq){
        User user = verifyUser(loginReq, DBConstants.USER_SOURCE_APP);
        return getApiLoginRes(loginReq, user);
    }

    /**
     * Web登录
     */
    public ApiLoginRes apiLoginByWeb(ApiLoginReq loginReq){
        User user = verifyUser(loginReq,DBConstants.USER_SOURCE_WEB);
        return getApiLoginRes(loginReq, user);
    }

    /**
     * 登陆校验用户的信息
     */
    public User verifyUser(ApiLoginReq loginReq, Byte source) {
        String phone = loginReq.getPhone();
        String type = loginReq.getType();

        if(!Constants.ALLOW_AUTH_TYPES.contains(type)){
            throw new BizException("未知的认证方式");
        }

        User user;
        if(type.equals(Constants.AUTH_TYPE_PWD)){
            user = userService.lambdaQuery().eq(User::getPhone, phone)//手机号
                    .or().eq(User::getAccount, phone)//账号
                    .or().eq(User::getEmail, phone)//邮箱
                    .one();
        }
        else {
            //校验电话格式
            if(!Validator.isMobile(phone)){
                throw new BizException("手机号号码格式错误");
            }
            user = userService.lambdaQuery().eq(User::getPhone, phone).one();
        }

        if(NullUtils.isNull(user)){
            throw new BizException("用户不存在");
        }

        if(DBConstants.BYTE_DISABLE.equals(user.getStatus())
                || DBConstants.USER_APPROVE_WAITING.equals(user.getApprove())){
            throw new BizException("该账号已被禁用 或者 该账号处于待审核");
        }

        if (DBConstants.USER_SOURCE_WEB.equals(source)) {
            String rolesByStr = user.getRolesIds();
            String permissionsByStr = user.getPermissionIds();
            if (!verifyRole(rolesByStr) && !verifyPermission(permissionsByStr)
                    && !verifyUserId(user.getUserId())) {
                throw new BizException("没有权限登录");
            }
        }

        return user;
    }

    /**
     * 验证权限
     */
    public boolean verifyPermission(String permissionsByStr) {
        JSONArray jsonArray = JSONArray.parseArray(permissionsByStr);
        List<String> permissionList = jsonArray.toJavaList(String.class);
        boolean isPass = false;
        for (String permission : permissionList) {
            if (loginPermissionsNames.contains(permission)) {
                isPass = true;
            }
        }
        return isPass;
    }

    /**
     * 验证角色
     */
    public boolean verifyRole(String rolesByStr) {
        JSONArray jsonArray = JSONArray.parseArray(rolesByStr);
        List<String> roleList = jsonArray.toJavaList(String.class);
        boolean isPass = false;
        for (String role : roleList) {
            if (loginRoleNames.contains(role)) {
                isPass = true;
            }
        }
        return isPass;
    }

    /**
     * 验证用户ID
     */
    public boolean verifyUserId(Long userId) {
        boolean isPass = false;
        if (configParams.getAllowUpdateOrderInfoUserIds().contains(String.valueOf(userId))) {
            isPass = true;
        }
        return isPass;
    }



    /**
     *  验证登陆的方式，通过就返回token实体
     */
    public ApiLoginRes getApiLoginRes(ApiLoginReq loginReq, User user) {
        String type = loginReq.getType();
        String originPassword = loginReq.getPassword();
        String smsCode = loginReq.getCode();
        String phone = loginReq.getPhone();

        if(type.equals(Constants.AUTH_TYPE_PWD)){
            if (StringUtils.isEmpty(originPassword)) {
                throw new BizException("密码为空");
            }
            //验证用户密码
            authByPassword(originPassword, user.getPassword());
        }
        else {
            if(StringUtils.isEmpty(smsCode)){
                throw new BizException("验证码为空");
            }
            //短信验证码验证
            authBySms(smsCode, phone);
        }

        String token = buildToken(user, TokenConstants.TOKEN);
        String refreshToken = buildToken(user, TokenConstants.REFRESH_TOKEN);

        redisService.setCacheObject(TokenConstants.TOKEN + user.getUserId(),
                token, TokenConstants.AUTH_TOKEN_EXPIRE_MIN , TimeUnit.MINUTES);
        redisService.setCacheObject(TokenConstants.REFRESH_TOKEN + user.getUserId(),
                refreshToken, TokenConstants.AUTH_REFRESH_TOKEN_EXPIRE_MIN , TimeUnit.MINUTES);

        //构建返回体
        ApiLoginRes response = new ApiLoginRes();
        response.setUserId(user.getUserId().intValue());
        response.setAccessToken(TokenConstants.PREFIX + token);
        response.setRefreshToken(TokenConstants.PREFIX + refreshToken);
        response.setTokenType(TokenConstants.PREFIX);
        response.setExpiresIn(TokenConstants.AUTH_TOKEN_EXPIRE_MIN);
        response.setUserVO(btoUserService.getUserInfo(user.getUserId()));
        return response;
    }


    /**
     * 刷新token
     */
    public String getToken (String refreshToken) {
        if (StringUtils.isBlank(refreshToken)) {
            throw new BizLoginException(401, "refresh_token不能为空, 重新登录！");
        }
        refreshToken = refreshToken.replace(TokenConstants.PREFIX, "");
        String userId = JwtUtils.getUserId(refreshToken);
        String redisKey = TokenConstants.REFRESH_TOKEN + userId;
        String redisToken = redisService.getCacheObject(redisKey);
        if (StringUtils.isNotEmpty(redisToken) && redisToken.equals(refreshToken)) {
            User user = userService.getById(userId);
            if (user == null) {
                throw new BizLoginException(401, BizCode.USER_ABSENT_ERROR.getMsg());
            }
            String redisKeyByBefore = TokenConstants.TOKEN + userId;
            String redisTokenByBefore = redisService.getCacheObject(redisKeyByBefore);
            if (StringUtils.isNotBlank(redisTokenByBefore)) {
                return TokenConstants.PREFIX + redisTokenByBefore;
            }

            String token = buildToken(user, TokenConstants.TOKEN);
            redisService.setCacheObject(TokenConstants.TOKEN + user.getUserId(),
                    token, TokenConstants.AUTH_TOKEN_EXPIRE_MIN , TimeUnit.MINUTES);
            return TokenConstants.PREFIX + token;
        }
        return null;
    }

    /**
     * 退出
     */
    public boolean loginOut () {
        Integer userId = ServletUtils.getHeaderToInt(Constants.HEADER_USER_ID);
        if (NullUtils.isNull(userId)) {
            throw new BizException("退出失败，请求头没有uid");
        }
        boolean deleteToken = redisService.deleteObject(TokenConstants.TOKEN + userId);
        boolean deleteRefreshToken = redisService.deleteObject(TokenConstants.REFRESH_TOKEN + userId);
        return deleteToken && deleteRefreshToken;
    }

    /**
     * 校验密码
     */
    private void authByPassword(String originPassword , String userPassword){
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encodeOriginPassword = bCryptPasswordEncoder.encode(originPassword);
        if(!bCryptPasswordEncoder.matches(userPassword, encodeOriginPassword)){
            throw new BizException("密码错误");
        }

    }

    private void authBySms(String authSmsCode, String phone){
        String code = redisService.getCacheObject(TokenConstants.SMS_CODE_LOGIN_KEY + phone);
        if (StringUtils.isEmpty(code)) {
            throw new BizException("验证码已过期，请输入重新获取验证码");
        }
        if (!authSmsCode.equals(code)) {
            throw new BizException("验证码错误，请输入正确的验证码");
        }
        redisService.deleteObject(TokenConstants.SMS_CODE_LOGIN_KEY + phone);
    }


    /**
    *  构建用户token 用户id 电话 角色 权限 相关信息
    */
    private String buildToken(User user, String type){
        Map<String , Object> map = new HashMap<>();
        map.put(TokenConstants.DETAILS_USER_ID , String.valueOf(user.getUserId()));
        map.put(TokenConstants.DETAILS_USERNAME , user.getName());
        map.put(TokenConstants.DETAILS_USER_PHONE , user.getPhone());

        if (TokenConstants.TOKEN.equals(type)) {
            map.put("expires" , TokenConstants.AUTH_TOKEN_EXPIRE_MIN);  //60
        } else if (TokenConstants.REFRESH_TOKEN.equals(type)) {
            map.put("expires" , TokenConstants.AUTH_REFRESH_TOKEN_EXPIRE_MIN);  //15天
        }
        return JwtUtils.createToken(map);
    }


}
